#!/usr/bin/python
 #-*-coding: utf-8 -*- 
# Script qui se connecte à la base de données, récupère les données, puis lançe l'apprentissage par le SOM
# Roxane Levy


import MySQLdb 
import sys
import ctypes
from numpy import array,linalg,zeros,nditer,sqrt,unravel_index
from pylab import plot,axis,show,pcolor,colorbar,bone
  
LIBRARY_PATH = './som.so'


#Fonction pour la création de la BDD
def getTarget() : 
	# Pour l'accès à la base de donnée,, modifiable par l'utilisateur
	HOST = 'localhost'
	USER = 'root'
	PASSWD = sys.argv[1]
	DATABASE = 'ADPD_Database'
	# connexion à la BDD
	db_connection = MySQLdb.connect(
       		 host=HOST,
      		 user=USER, 
       		 passwd=PASSWD, 
       		 )

	# il faut un curseur pour exécuter la commande
	cursor = db_connection.cursor()
	cursor.execute('USE ADPD_Database')
        cursor.execute('SELECT espece FROM Iris')   # selection du champ cible
         
        data = cursor.fetchall()
        t = [] 
        for row in data :
		t.append(row[0])  # on place dans t la valeur du champ espece de chaque vecteur de donnée


	cursor.execute('SELECT color1,color2,color3,color4 FROM Iris')   #selection des données
	data = cursor.fetchall()
        t.insert(0,len(data[0]))  # récupère le nombre de champs

	try:
  		 db_connection.commit()
	except:
  		 db_connection.rollback()

	db_connection.close()
	return array(t)




def distance_map(weights):
        """ Fonction qui retourne une map contenant la moyenne des distances entre les poids de neurones."""
        distance_map = zeros((weights.shape[0],weights.shape[1]))
        iteration = nditer(distance_map, flags=['multi_index'])
        while not iteration.finished:
            for i in range(iteration.multi_index[0]-1,iteration.multi_index[0]+2):
                for j in range(iteration.multi_index[1]-1,iteration.multi_index[1]+2):
                    if i >= 0 and i < weights.shape[0] and j >= 0 and j < weights.shape[1]:
                        distance_map[iteration.multi_index] += linalg.norm(weights[i,j,:]-weights[iteration.multi_index])
            iteration.iternext()
        distance_map = distance_map/distance_map.max()
        return distance_map




def findbestmatchingnode(vector,weights,activation_map) :
      """Fonction qui retourne les coordonnées du BMU """
      for a in range(len(weights)) : 
	   for b in range(len(weights[a])) : 
		activation_map[a][b] = linalg.norm(euclidian(weights[a][b],vector))
      return unravel_index(activation_map.argmin(),activation_map.shape) 



def euclidian(vectorMap, vectorData) :
	""" Fonction qui retourne la distance euclidienne entre deux vecteurs """
	distance = 0 
        for i in range(len(vectorMap)) :
 		distance += (vectorData[i] - vectorMap[i]) * (vectorData[i] - vectorMap[i])
	dist = sqrt(distance)
	return dist



  
def main(target, numberInputVec,neuroneSize,map_x,map_y,nb_iter,rateDecay,typeNeigh,flagshuffle):
    '''Main entry point'''
  
    # Charge la librairie du som
    my_library = ctypes.CDLL(LIBRARY_PATH)

    
    # On indique les arguments des fonctions
    my_library.startprog.argtypes = [ctypes.c_int, ctypes.c_int,ctypes.c_int,ctypes.c_int,ctypes.c_int,ctypes.c_int,ctypes.c_int, ctypes.c_int]
    my_library.mainRoutine.argtypes = [ctypes.c_char_p]  
    my_library.getData.argtypes = []
    my_library.getRN.argtypes = []
    my_library.getActiMap.argtypes = []

    
    
    # On indique maintenant le type de retour des fonctions
    my_library.startprog.restype = ctypes.c_int
    my_library.mainRoutine.restype = ctypes.c_int   # toujours vérifier que le main du prog C retourne 0
    my_library.getData.restype = ctypes.POINTER(ctypes.POINTER(ctypes.c_float *neuroneSize) * numberInputVec)   # recevoir les vecteurs de données
    my_library.getRN.restype = ctypes.POINTER(ctypes.POINTER(ctypes.POINTER(ctypes.c_float * neuroneSize) * map_y ) * map_x )  # recevoir le RN
    my_library.getActiMap.restype = ctypes.POINTER(ctypes.POINTER(ctypes.c_float * map_y) * map_x)   # recevoir l'activation map
    my_library.destroyAll.restype = ctypes.c_int    


    # On envoie les paramètres au programme C
    init = my_library.startprog(numberInputVec,neuroneSize,map_x,map_y, nb_iter, rateDecay, typeNeigh, flagshuffle)
    print 'Initialisation... '
    if (init != 0) : exit("Il y a eu une erreur dans l'initialisation des paramètres\n")

    # Appel à la main routine du SOM (apprentissage)
    main_result = my_library.mainRoutine(sys.argv[1])
    if (main_result != 0) : exit("error in main")

    # On récupère les données normalisées
    c_array = my_library.getData()   

    vec_data = zeros((numberInputVec,neuroneSize))
    index = 0 
    row = 0
    for i in c_array.contents : 
	for j in i.contents : 
		vec_data[index][row] = j
		row += 1
	index +=1
        row = 0

    # On récupère la map
    e_array = my_library.getRN()
    index1 = 0
    index2=0
    index3 = 0
    weights = zeros((map_x,map_y,neuroneSize))
    for i in e_array.contents :
	for j in i.contents :
                for k in j.contents :
			weights[index1][index2][index3] = k
			index3+= 1
		index2 += 1
		index3 = 0 
        index1 += 1
        index2 = 0


    # On récupère l'activation map
    f_array = my_library.getActiMap()
    activation_map = zeros((map_x,map_y))
    index = 0 
    row = 0
    for i in f_array.contents : 
	for j in i.contents : 
		activation_map[index][row] = j
		row += 1
	index +=1
        row = 0

    print 'Les données et le réseau neuronal ont été récupérés'
 
    erase = my_library.destroyAll()
    if (erase != 0) : exit("Erreur dans la libération de mémoire")

    print 'Start plotting...'

    bone()  #set the default colormap to bone 
   

    pcolor(distance_map(weights).T) # pcolor crée un pseudocolor plot d'une matrice 2-D (la distance map comme background), .T est pour transposer la matrice 
    colorbar() # Add a colorbar to plot. 
    t = zeros(len(target),dtype=int)
    # pour chaque espèce correspond un nombre
    t[target == 'Iris-setosa'] = 0 
    t[target == 'Iris-versicolor'] = 1
    t[target == 'Iris-virginica'] = 2
    # on utilise un marqueur et une couleur pour chaque espèce
    markers = ['o','s','D']
    colors = ['r','g','b']

    for idx,vec in enumerate(vec_data):   #idx est le numéro du vecteur d'apprentissage, vec est le vecteur d'apprentissage
 	bmu = findbestmatchingnode(vec,weights,activation_map) 
 	# Pour chaque vecteur d'entrée, on place un marqueur sur la position gagnante
 	plot(bmu[0]+.5,bmu[1]+.5,markers[t[idx]],markerfacecolor='None',markeredgecolor=colors[t[idx]],markersize=12,markeredgewidth=2)

    axis([0,weights.shape[0],0,weights.shape[1]])
    show() # affichage




if __name__ == '__main__':
    if (len(sys.argv) < 8) :
		print "usage:%s PASSWORD MAP_X MAP_Y NOMBRE_ITERATIONS RATE_DECAY LEARNING_TYPE FLAGSHUFFLE" %sys.argv[0]
		exit(0)
    target = getTarget()

    main(target[1:],len(target[1:]),int(target[0]),int(sys.argv[2]),int(sys.argv[3]),int(sys.argv[4]),int(sys.argv[5]),int(sys.argv[6]), int(sys.argv[7]))
